package cn.org.wifit.imserver.zookeeper.manager;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import cn.org.wifit.core.zookeeper.ServerNodeTree;
import cn.org.wifit.core.zookeeper.manager.ZookeeperReadManager;
import cn.org.wifit.imserver.boot.BootStartup;

/**
 * private static final ZookeeperIMServerWriteManager IMSERVER_WRITE_MANAGER = new ZookeeperIMServerWriteManager();<br>
 * 本来应该使用自主的单例模式，但是当引入了spring框架后，我们可以使用spring的配置文件，将zookeeperReadManager设置为单例模式<br>
 * <br>
 * ZookeeperIMServerWriteManager用于帮助每个IMServer在zookeeper服务集群上建立自己的专属节点，包括：<br>
 * /IMServer<br>
 * /IMServer/server<br>
 * /IMServer/server/ipx<br>
 * 并且负责判断这些将要创建的节点的正确性。
 * @author wenjie
 */
public class ZookeeperIMServerWriteManager {
	/**
	 * 由spring托管的一个单例对象
	 */
	private ZooKeeper zk;
	
	/**
	 * 既是配置文件中的imserver.name
	 */
	private String servername;
	
	/**
	 * 既是配置文件中的imserver.fromid
	 */
	private Integer fromid;
	
	/**
	 * 既是配置文件中的imserver.toid
	 */
	private Integer toid;

	public ZookeeperIMServerWriteManager() {
		
	}
	
	/**
	 * 在远程zookeeper集群上创建这个IMServer所代表的叶子节点,能够表述这个服务器自身特性的节点
	 * @param ip 指定的IMServer的公网IP
	 * @param udp 指定的IMServer的udp监听所使用的端口
	 * @param tcp 指定的IMServer的tcp监听所使用的端口
	 * @return 如果创建成功，则返回true；其他情况返回false
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	public boolean createZookeeperNode(String ip , Integer udp , Integer tcp) throws Exception {
		/*
		 * 1、添加前需要进行检查远程zookeeper的状态。以下情况不能添加，报错，终止运行
		 * 当zookeeper上面已经有了和fromid——toid重合的范围，但是范围又不是完全重合
		 * 
		 * 2、以下情况需要添加server节点后，在进行ipx节点的添加
		 * 当zookeeper上面没有任何id范围与现在的id范围重合
		 * 
		 * 3、以下情况需要在已有的server节点下直接添加ipx节点
		 * 当zookeeper上面fromid——toid的范围和将要添加的fromid——toid的范围刚好重合
		 * */
		if(fromid >= toid) {
			throw new RuntimeException("fromid >= toid  , 请检查您启动时设置的参数！");
		}
		//这个变量说明是1、2、3哪种情况，以便进行相应的操作
		int condition = 2;
		
		List<String> serverFullPathList = this.zk.getChildren("/IMServer", false);
		int index  = 0;
		String serverSequence = "";
		for(; serverFullPathList != null && index < serverFullPathList.size() ; index++) {
			String serverPath = serverFullPathList.get(index);
			byte[] datas = this.zk.getData("/IMServer/" + serverPath, false, null);
			
			ObjectInputStream objectInput = new ObjectInputStream(new ByteArrayInputStream(datas));
			HashMap<String, Integer> dataMap = (HashMap<String, Integer>)objectInput.readObject();
			
			//获取各参数
			Integer _fromid = dataMap.get("fromid");
			Integer _toid = dataMap.get("toid");
			//如果条件成立，说明fromid——toid的范围在_fromid——_toid的范围之外
			if(this.fromid.intValue() == _fromid && this.toid.intValue() == _toid) {
				serverSequence = serverPath;
				condition = 3;
				break;
			}
			else if(this.fromid.intValue() < _fromid && this.toid.intValue() < _fromid 
				|| (this.fromid.intValue() > _toid && this.toid.intValue() > _toid)) {
				continue;
			} else {
				condition = 1;
				throw new RuntimeException("错误的id范围，在" + index +"处");
			}
		}
		
		//说明要先添加server节点，再添加ipx节点
		if(condition == 2) {
			//先创建Server节点
			HashMap<String, Integer> datas = new HashMap<String , Integer>();
			datas.put("fromid", this.fromid);
			datas.put("toid", this.toid);
			ByteArrayOutputStream byteout = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(byteout);
			out.writeObject(datas);
			byte[] objectContext = byteout.toByteArray();
			byteout.close();
			out.close();
			
			serverSequence = this.zk.create("/IMServer/server", objectContext, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
			
			//然后创建server节点下的ipx节点
			HashMap<String , Object> ipxdatas = new HashMap<String , Object>();
			ipxdatas.put("ip", ip);
			ipxdatas.put("port", udp);
			ipxdatas.put("userport", tcp);
			byteout = new ByteArrayOutputStream();
			out = new ObjectOutputStream(byteout);
			out.writeObject(ipxdatas);
			objectContext = byteout.toByteArray();
			byteout.close();
			out.close();
			
			this.zk.create(serverSequence + "/ipx", objectContext, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
		}
		
		//说明在指定的server下，直接添加ipx节点
		if(condition == 3) {
			//创建server节点下的ipx节点
			HashMap<String , Object> ipxdatas = new HashMap<String , Object>();
			ipxdatas.put("ip", ip);
			ipxdatas.put("port", udp);
			ipxdatas.put("userport", tcp);
			ByteArrayOutputStream byteout = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(byteout);
			out.writeObject(ipxdatas);
			byte[] objectContext = byteout.toByteArray();
			byteout.close();
			out.close();
			this.zk.create("/IMServer/" + serverSequence + "/ipx", objectContext , Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
		}
		
		return true;
	}
	
	/**
	 * 该方法根据远程zookeeper的节点情况，初始化本地xml缓存的创建，即创建ServerNodeTree内存树
	 * @return 如果初始化成功，则返回true；其他情况返回false
	 */
	@SuppressWarnings("unchecked")
	public boolean createLocalServerNodeTree() throws Exception {
		FileSystemXmlApplicationContext springContext = BootStartup.context;
		ZookeeperReadManager zookeeperReadManager = (ZookeeperReadManager)springContext.getBean("zookeeperReadManager");
		ZooKeeper zooKeeper = zookeeperReadManager.getZk();
		ServerNodeTree serverNodeTree = ServerNodeTree.getNewInstance();
		
		//==========================这里进行ServerNodeTree对象的初次组建（根据zookeeper上面的初次集群信息）
		//首先是IMUser
		try {
			List<String> ipxList = zooKeeper.getChildren("/IMUser/ipxs", false);
			for(int index = 0 ; ipxList != null && index < ipxList.size() ; index++) {
				String ipxpath = ipxList.get(index);
				
				byte[] datas=  zooKeeper.getData("/IMUser/ipxs" + ipxpath, false, null);
				ObjectInputStream objectInput = new ObjectInputStream(new ByteArrayInputStream(datas));
				HashMap<String, Object> dataMap = (HashMap<String, Object>)objectInput.readObject();
				objectInput.close();
				
				//获取各参数
				String ipxSequence = ipxpath.substring(ipxpath.length() - 10, ipxpath.length());
				String ip = dataMap.get("ip").toString();
				Integer port = (Integer)dataMap.get("port");
				
				serverNodeTree.AddIMUserServerNode(ipxSequence, ip, port);
			}
		} catch(Exception e) {
			e.printStackTrace(System.out);
			return false;
		}
		
		//然后是IMServer节点
		try {
			List<String> serverList = zooKeeper.getChildren("/IMServer", false);
			for(int index = 0 ; serverList != null && index < serverList.size() ; index++) {
				String serverPath = serverList.get(index);
				
				byte[] datas=  zooKeeper.getData("/IMServer/" + serverPath, false, null);
				ObjectInputStream objectInput = new ObjectInputStream(new ByteArrayInputStream(datas));
				HashMap<String, Integer> dataMap = (HashMap<String, Integer>)objectInput.readObject();
				objectInput.close();
				
				//获取各参数
				String serverSequence = serverPath.substring(serverPath.length() - 10, serverPath.length());
				Integer fromid = dataMap.get("fromid");
				Integer toid = dataMap.get("toid");
				//构造server节点
				boolean isok = serverNodeTree.AddIMServer(serverSequence, fromid, toid);
				//TODO 这里是不是应该抛出异常
				if(!isok) {
					continue;
				}
				
				List<String> ipxList = zooKeeper.getChildren("/IMServer/" + serverPath , false);
				for(int sum = 0 ; ipxList != null && sum < ipxList.size() ; sum++) {
					String ipxpath = ipxList.get(sum);
					
					datas=  zooKeeper.getData("/IMServer/" + serverPath + "/" + ipxpath , false, null);
					objectInput = new ObjectInputStream(new ByteArrayInputStream(datas));
					HashMap<String, Object> ipxdatas = (HashMap<String, Object>)objectInput.readObject();
					objectInput.close();
					
					//获取各参数
					String ipxSequence = ipxpath.substring(ipxpath.length() - 10, ipxpath.length());
					String ip = ipxdatas.get("ip").toString();
					Integer port = (Integer)ipxdatas.get("port");
					Integer userport = (Integer)ipxdatas.get("userport");
					
					//TODO 这里是不是应该抛出异常
					isok = serverNodeTree.AddIMServerIPX(serverSequence, ipxSequence, ip, port, userport);
					if(!isok) {
						break;
					}
				}
			}
		} catch(Exception e) {
			e.printStackTrace(System.out);
			return false;
		}
		
		return true;
	}
	
	public ZooKeeper getZk() {
		return zk;
	}

	public void setZk(ZooKeeper zk) {
		this.zk = zk;
	}

	public String getServername() {
		return servername;
	}

	public void setServername(String servername) {
		this.servername = servername;
	}

	public Integer getFromid() {
		return fromid;
	}

	public void setFromid(Integer fromid) {
		this.fromid = fromid;
	}

	public Integer getToid() {
		return toid;
	}

	public void setToid(Integer toid) {
		this.toid = toid;
	}
}